# app/dao/expense_first_cate_dao.py
from app.db import db  # 从独立的 db 模块导入
from app.Models.FinanceModels.ExpenseFirstCate import ExpenseFirstCate  # 按实际模型路径调整
from sqlalchemy.exc import IntegrityError  # 捕获数据库唯一性约束异常


class ExpenseFirstCateDAO:
    """支出一级分类模型的数据访问对象"""

    @staticmethod
    def create_first_cate(first_cate_name):
        """
        新增支出一级分类
        :param first_cate_name: 一级分类名称（如"餐饮美食"、"交通出行"）
        :return: 新增的一级分类对象
        :raises ValueError: 名称为空、重复或数据库异常时抛出
        """
        # 1. 基础校验：名称非空（去除首尾空格，避免无效重复）
        clean_name = first_cate_name.strip() if first_cate_name else ""
        if not clean_name:
            raise ValueError("支出一级分类名称不能为空")

        # 2. 唯一性校验：避免同名称分类重复创建
        existing_cate = ExpenseFirstCateDAO.get_first_cate_by_name(clean_name)
        if existing_cate:
            raise ValueError(f"支出一级分类已存在：{clean_name}")

        # 3. 数据库新增操作（捕获约束异常，双重保障）
        try:
            new_cate = ExpenseFirstCate(first_cate_name=clean_name)
            db.session.add(new_cate)
            db.session.commit()
            return new_cate
        except IntegrityError:
            db.session.rollback()  # 回滚事务，避免会话状态异常
            raise ValueError(f"创建失败：分类名称'{clean_name}'已存在（数据库约束）")
        except Exception as e:
            db.session.rollback()
            raise ValueError(f"数据库异常：{str(e)}")

    @staticmethod
    def get_first_cate_by_id(cate_id):
        """
        根据ID查询支出一级分类
        :param cate_id: 一级分类ID
        :return: 一级分类对象（存在则返回，不存在返回None）
        """
        return ExpenseFirstCate.query.get(cate_id)

    @staticmethod
    def get_first_cate_by_name(cate_name):
        """
        根据名称查询支出一级分类（用于唯一性校验、精准查询）
        :param cate_name: 一级分类名称（自动去除首尾空格）
        :return: 一级分类对象（存在则返回，不存在返回None）
        """
        if not cate_name:
            return None
        clean_name = cate_name.strip()
        return ExpenseFirstCate.query.filter_by(first_cate_name=clean_name).first()

    @staticmethod
    def get_all_first_cates():
        """
        查询所有支出一级分类（按ID升序，保证展示顺序稳定）
        :return: 一级分类列表（空列表表示无数据）
        """
        return ExpenseFirstCate.query.order_by(ExpenseFirstCate.id.asc()).all()

    @staticmethod
    def update_first_cate(cate_id, new_name):
        """
        更新支出一级分类名称
        :param cate_id: 待更新的一级分类ID
        :param new_name: 新的分类名称
        :return: 更新后的一级分类对象
        :raises ValueError: 分类不存在、名称为空或重复时抛出
        """
        # 1. 校验分类是否存在
        cate = ExpenseFirstCateDAO.get_first_cate_by_id(cate_id)
        if not cate:
            raise ValueError(f"支出一级分类不存在（ID：{cate_id}）")

        # 2. 校验新名称合法性
        clean_new_name = new_name.strip() if new_name else ""
        if not clean_new_name:
            raise ValueError("新分类名称不能为空")

        # 3. 校验新名称是否与其他分类重复（排除自身）
        existing_cate = ExpenseFirstCateDAO.get_first_cate_by_name(clean_new_name)
        if existing_cate and existing_cate.id != cate_id:
            raise ValueError(f"新名称'{clean_new_name}'已被其他分类使用")

        # 4. 执行更新
        try:
            cate.first_cate_name = clean_new_name
            db.session.commit()
            return cate
        except IntegrityError:
            db.session.rollback()
            raise ValueError(f"更新失败：新名称'{clean_new_name}'已存在")
        except Exception as e:
            db.session.rollback()
            raise ValueError(f"数据库异常：{str(e)}")

    @staticmethod
    def delete_first_cate(cate_id, check_second_cate=False, second_cate_dao=None):
        """
        删除支出一级分类（支持关联二级分类校验，避免数据引用错误）
        :param cate_id: 待删除的一级分类ID
        :param check_second_cate: 是否校验该分类下是否有二级分类（True/False）
        :param second_cate_dao: 支出二级分类DAO（check_second_cate=True时必填）
        :return: True（删除成功）
        :raises ValueError: 分类不存在、被二级分类引用或删除失败时抛出
        """
        # 1. 校验分类是否存在
        cate = ExpenseFirstCateDAO.get_first_cate_by_id(cate_id)
        if not cate:
            raise ValueError(f"支出一级分类不存在（ID：{cate_id}）")

        # 2. 可选：校验是否关联二级分类（避免删除有子分类的父分类）
        if check_second_cate:
            if not second_cate_dao:
                raise ValueError("开启二级分类校验时，需传入 second_cate_dao")
            # 调用二级分类DAO查询该一级分类下的子分类
            related_seconds = second_cate_dao.get_seconds_by_first_cate(cate_id)
            if related_seconds:
                raise ValueError(
                    f"无法删除：该分类下存在{len(related_seconds)}个二级分类（需先删除子分类）"
                )

        # 3. 执行删除
        try:
            db.session.delete(cate)
            db.session.commit()
            return True
        except Exception as e:
            db.session.rollback()
            raise ValueError(f"删除失败：{str(e)}")